-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multiple domains support #3870
base: main
Are you sure you want to change the base?
Multiple domains support #3870
Conversation
My first question would be, why change And a dev tip, install and activate |
HostBecause it definitely isn't a host. format!("{protocol}://{host}") This is a base URL, but you could say, that domain might also not be the correct term. My reasoning was, that domain is the term used for the config. Pre-CommitI ran |
This comment was marked as resolved.
This comment was marked as resolved.
It does not store a host. It stores a base url. If it stored a host the extractor would be: let headers = ...;
if let Some(host) = headers.get_one("X-Forwarded-Host") {
host
} else if let Some(host) headers.get_one("Host") {
host
} else {
panic!("invalid http request");
} But it's not, the extractor gets a URL from a combination of protocol, host and path (from config.domain()). It doesn't store |
It definitely isn't a domain, a domain is the last 2 (or 3) parts. Edit: so actually, our config is wrong too 😅 |
I know, but the precedent is set in
This definitely isn't a domain, but that's what the precedent says. We combine a host with a protocol and possibly a path into a URL. |
I understand, but the location where you changed it has nothing to do with config, but with the headers. |
So then the correct term would be BaseURL right?? I renamed it, because in the optimal situation (the domain is configured) the struct Stores the value from the But all I really want to know is if this approach (looking up the domain by host), not implementation, vibes with the project vision. |
But, maybe we need to wait for the whole picture. But because we didn't do it correctly in one place, doesn't mean we shouldn't at others or improve. Else we should just make it more clear using comments for other developers maybe. But i would prefer to use the right naming where possible when we adjust the code. |
There are some issues. Mails can be sent without any HTTP connection, so there must be one main host/domain in the config which will be used in those cases. Using a different host/domain for mails when there is a HTTP connection will become confusing. So, the only location where i think it will be useful for are MFA and attachments/sends. |
That's why I added |
Checks are passing now, but I still have some cleanup to be done. Probably will do it tomorrow, after that it should be ready for review. To do (at least):
Behaviours which may need to change (I think they're fine)
|
From my perspective I would say this is ready to review. |
@BlackDex Sorry for the ping, but is anything gonna happen with this PR? Because if not, it should probably just be closed. |
It has to wait for a check. As i mentioned in a few other pr's I'm busy with other stuff (for this project). But i just don't have much time currently. Just be patient and my comments (or other people's comments) will follow eventually. |
the domain chosen is always the first domain
if host isn't an allowed domain, we default to the main domain.
since it just returns a const
43a7b7f
to
c150818
Compare
I still think naming of a few items is still off and could be changed to better match the RFC naming. Some items could be moved to So all in all a lot of items to address i think. @BlockListed If you still want to work on this let me know and i will add more specific comments to the code. |
Yeah, I'd still be willing to work on the PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, have added some comments on some items which i think are mainly an issue.
But in general i think this needs to be revisited on how to solve this.
We want to prevent as much as possible to calculate the the full URL like https://domain.com/vault
or https://vault.domain.com
or the origin all the time during every call.
For the JWT/MFA this probably need to be dynamic too.
If i use WebAuthn or Duo, i would want to be able to login using those methods via all allowed domains, not only the main one.
Sending emails (especially the ones automated) can of course only be from the main domain. But some of course could be from the domain which the user logged-in on is using. While I'm not fully sure that is what we want though. Force the main domain here does sound more logical in general.
Some quick thoughts maybe.
A hashmap which could quickly match a domain to an origin
Mabye also for path, to match/fetch it for the specific domain.
Also, i have not checked the actual functionally.
@@ -12,6 +12,7 @@ use rocket::{ | |||
Catcher, Route, | |||
}; | |||
|
|||
use crate::auth::HostInfo; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be moved a few lines down to line 21.
@@ -988,7 +1008,7 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> { | |||
} | |||
|
|||
/// Extracts an RFC 6454 web origin from a URL. | |||
fn extract_url_origin(url: &str) -> String { | |||
pub fn extract_url_origin(url: &str) -> String { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would not make this public and use this function at all the other places.
It probably is better for the main_domain
to have an extra entry in the config like domain_origin
, so main_domain_origin
probably. That way it is only generated once when the config reloads and not all the time.
Also, within config.rs
it might best be that we change auto
to gen
to ensure it is generated during config load.
fn extract_origins(urls: &str) -> String { | ||
let mut origins = urls | ||
.split(',') | ||
.map(extract_url_origin) | ||
// TODO add itertools as dependency maybe | ||
.fold(String::new(), |mut acc, origin| { | ||
acc.push_str(&origin); | ||
acc.push(','); | ||
acc | ||
}); | ||
|
||
// Pop trailing comma | ||
origins.pop(); | ||
|
||
origins | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what you are doing here. It looks like it is only generating the domain_origin
value which in the end looks like to never be used only to validate if the amount when split matches domain
. Which should, since it is generated. Maybe it doesn't right now because the value is set to auto
, so i think it would be best to set it to gen
and always generate the value. But, it should then be renamed to something like domain_origin_main
probably, and only extract the main element?
All other logic should probably be done within the HostInfo
i think.
if embed_images { | ||
"cid:".to_string() | ||
} else { | ||
let domain = domains.split(',').next().expect("Domain missing"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When creating a domain_main
variable which will hold the value for of the first item in the list, there should be no need for splitting here any more, and just directly use that special value.
/// Domain path |> Domain URL path (in https://example.com:8443/path, /path is the path) | ||
domain_path: String, false, auto, |c| extract_url_path(&c.domain); | ||
/// MUST be the same for all domains. | ||
domain_path: String, false, auto, |c| extract_url_path(c.domain.split(',').next().expect("Missing domain")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be different per domain of course. So assuming the path only from the first domain is probably not the best solution here.
it might be that for example:
https://shared.domain.local/vault
https://vault.domain.tld
are configured, or vice-versa. Which would either never add /vault
or always add /vault
and sometimes this would be an error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have added several log::
entries here. log::
can be removed since those macro's should be callable directly.
But logging in this location could slow down the application a lot. Even though it only should do this during debug, it will do this for every call which will do something with that Struct.
If it is really needed to have this info here, i would then suggest to place it under trace!
instead.
Hey So I'm the contributor maintaining the OpenID Connect PR (#3899) and I'm wondering how both feature would interact. For the SSO, the config is global (As opposed to official Bitwarden where it's per Organization) since I believe it makes more sense in the context of smaller deployment. The easiest solution would probably be that all domains still use the same SSO, but I'm guessing some user will want more integration between both feature. On the other end integrating both feature at the organization level would make it closer to how it works in Bitwarden and might allow reusing things like Domain Verification (which appear heavily linked to SSO but probably not 100% since it's in a separate config section). |
@Timshel I actually think it might be useful for SSO/OIDC. If an environment is not designed for multiple domains, then admins just need to configure a single domain and not multiple domains of course. |
Yeah I've got some stuff on my plate right now. Will try to start making changes on the weekend. Sorry for not making that more clear. |
Fixes #2690
Very WIP PR, I just want some feedback about my approach for now.
I am planning to go with the allowed domains approach.
Overview:
We create 2 Hashmaps, which map the Host header to either Domain or Origin.Limitations:
Future:
- Change JWT system to create tokens, which work for a single domain.